package com.leenmvc.core.dao.communal;

import com.leenmvc.core.annotation.FillField;
import com.leenmvc.core.base.BaseEntity;
import com.leenmvc.core.init.Constant;
import com.leenmvc.core.utils.BeanUtils;
import com.leenmvc.core.utils.ConvertUtils;
import com.leenmvc.core.utils.StringUtils;
import com.leenmvc.core.utils.collection.ArrayUtils;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * sql语句包装类的父类
 */
public abstract class Wrapper {

    protected static final Map<String, String> sqlCache = new ConcurrentHashMap<>();

    public Wrapper() {};

    protected StringBuilder sql = new StringBuilder();

    protected Class<?> cls = null;

    /* 将要被查出来的主表中的字段 */
    protected List<String> columns = new ArrayList<String>();

    /** 主表中含有的字段*/
    protected List<String> fields = new ArrayList<String>();
    /** 主表中的Field列表*/
    protected List<Field> fieldList = new ArrayList<Field>();

    /* 将要被查出来的非主表中的字段 */
    protected List<String> outColumns = new ArrayList<String>();

    /** 非主表中含有的一对一字段*/
    protected Field[] oneToOneFields =  new Field[0];

    /** 非主表中含有的字段一对多关系*/
    protected Field[] oneToMulFields = new Field[0];

    /** 非主表中含有的字段一字段填充 */
    protected Field[] oneFieldInFields =  new Field[0];

    protected Field[] outFields =  new Field[0];

    /** where条件*/
    protected StringBuffer where = new StringBuffer();

    protected StringBuffer orderBy = new StringBuffer();

    protected int[] limit = new int[]{-1, -1};

    /** 参数列表（专属容器） */
    protected List<Object> paramList = new ArrayList<Object>();

    /* 存储条件列表(专属中介容器) linkor专用？？ */
    protected List<String> conditons = new ArrayList<String>();

    /* class对应的简单名字 */
    protected String simpleName = null;

    // and 连接符
    public abstract Wrapper and();

    // 如果传参数那么默认就是用等号连接
    public abstract Wrapper and(String field, Object value);

    public abstract Wrapper or();

    public abstract Wrapper linkOr();

    public abstract Wrapper joinField(String field);

    public abstract Wrapper joinOne(String field);

    public abstract Wrapper joinMul(String field);

    public abstract Wrapper or(String field, Object value);

    public abstract Wrapper count(Class<?> cls);

    public abstract String getSql();

    public abstract Wrapper orderBy(String field, String desc);

    public abstract Wrapper decField(String... fields);

    public abstract Wrapper incField(String... fields);

    public abstract Wrapper addSet(String setKey, Object value);

    public abstract Wrapper limit(Integer... pageInfo);

    /**
     * 编辑SQL语句的条件 等于
     *
     * @param field
     * @param value
     * @return
     */
    public Wrapper eq(String field, Object value) {
        setAnd();
        Field field1 = BaseEntity.getField(simpleName + Constant.DOT  + field);
        // 如果是外键，那么通过外键关联
        FillField fillField = field1.getAnnotation(FillField.class);
        if(fillField == null) {
            field = simpleName + Constant.DOT + BaseEntity.humpLine(field);
        } else {
            field = fillField.entity().getSimpleName() + Constant.DOT + BaseEntity.humpLine(field);
        }

        if(StringUtils.endsWithIgnoreCase(this.where, "or)")) {
            this.where.insert(this.where.length() - 1, " "+ field + Constant.QUESTION_EQU_MARK_SIGN);
            int last = this.where.lastIndexOf("(");
            conditons.add(this.where.substring(last));
        } else {
            this.where.append(field + Constant.QUESTION_EQU_MARK_SIGN);
            conditons.add(BaseEntity.humpLine(field) + Constant.QUESTION_EQU_MARK_SIGN);
        }
        paramList.add(value);

        return null;
    };

    /**
     * 编辑SQL语句的条件 不等于
     *
     * @param field
     * @param value
     * @return
     */
    public Wrapper uEq(String field, Object value) {
        setAnd();
        field = simpleName + Constant.DOT + BaseEntity.humpLine(field);
        if (StringUtils.endsWithIgnoreCase(this.where, "or)")) {
            this.where.insert(this.where.length() - 1, " " + field + " != ?");
            int last = this.where.lastIndexOf("(");
            conditons.add(this.where.substring(last));
        } else {
            this.where.append(field).append(" != ").append(Constant.QUESTION_MARK_SIGN);
            conditons.add(field + " != ?");
        }
        paramList.add(value);
        return null;
    };

    /**
     * 编辑SQL语句的条件  右像
     * @param field
     * @param value
     * @return
     */
    public Wrapper likeR(String field, Object value) {
        setAnd();
        field = simpleName + Constant.DOT + BaseEntity.humpLine(field);
        if (StringUtils.endsWithIgnoreCase(this.where, "or)")) {
            this.where.insert(this.where.length() - 1, " " + field + " like '" + value + "%'");
            int last = this.where.lastIndexOf("(");
            conditons.add(this.where.substring(last));
        } else {
            this.where.append(field).append(" like '").append(value).append("%'");
            conditons.add(field + " like '" + value + "%'");
        }
        return null;
    };

    public Wrapper likeL(String field, Object value) {
        setAnd();
        field = simpleName + Constant.DOT + BaseEntity.humpLine(field);
        if (StringUtils.endsWithIgnoreCase(this.where, "or)")) {
            this.where.insert(this.where.length() - 1, " " + field + " like '%" + value + "'");
            int last = this.where.lastIndexOf("(");
            conditons.add(this.where.substring(last));
        } else {
            this.where.append(field).append(" like '%").append(value).append("'");
            conditons.add(field + " like '%" + value + "'");
        }
        return null;
    };

    public Wrapper like(String field, Object value) {
        setAnd();
        value = value.toString().replaceAll("([%'\\\\])", "\\\\$1");
        Field field1 = BaseEntity.getField(simpleName + Constant.DOT  + field);
        // 如果是外键，那么通过外键关联
        FillField fillField = field1.getAnnotation(FillField.class);
        if(fillField == null) {
            field = simpleName + Constant.DOT + BaseEntity.humpLine(field);
        } else {
            field = fillField.entity().getSimpleName() + Constant.DOT + BaseEntity.humpLine(field);
        }
        if (StringUtils.endsWithIgnoreCase(this.where, "or)")) {
            this.where.insert(this.where.length() - 1, " " + field + " like '%" + value + "%'");
            int last = this.where.lastIndexOf("(");
            conditons.add(this.where.substring(last));
        } else {
            this.where.append(field).append(" like ").append("'%").append(value).append("%'");
        }
        return null;
    };

    public Wrapper in(String field, Object[] ins) {
        setAnd();
        if (ins.length == 0) {
            ins = ArrayUtils.arrayAdd(ins, "999999999999999999999");
        }
        field = simpleName + Constant.DOT + BaseEntity.humpLine(field);
        if (StringUtils.endsWithIgnoreCase(this.where, "or)")) {
            this.where.insert(this.where.length() - 1, " " + field + " in (" + StringUtils.join(ins, Constant.COMMA) + ")");
            int last = this.where.lastIndexOf("(");
            last = this.where.lastIndexOf("(", last - 1);
            conditons.add(this.where.substring(last));
        } else {
            this.where.append(field).append(" in (" + StringUtils.join(ins, Constant.COMMA) + ")");
            conditons.add(field + " in (" + StringUtils.join(ins, Constant.COMMA) + ")");
        }
        return null;
    };

    public <T> Wrapper in(String field, List<T> ins) {
        return this.in(field, ins.toArray());
    };

    public Wrapper between(String field, Object start, Object end) {
        field = simpleName + Constant.DOT + BaseEntity.humpLine(field);
        if (StringUtils.endsWithIgnoreCase(this.where, "or)")) {
            this.where.insert(this.where.length() - 1, " " + field + " between " + start + Constant.BLANK_AND + end);
            int last = this.where.lastIndexOf("(");
            last = this.where.lastIndexOf("(", last - 1);
            conditons.add(this.where.substring(last));
        } else {
            this.where.append(field).append(" between ").append(start).append(Constant.BLANK_AND).append(end);
            conditons.add(field + " between " + start + Constant.BLANK_AND + end);
        }
        return null;
    };

    public Wrapper lt(String field, Object value) {
        setAnd();
        field = simpleName + Constant.DOT + BaseEntity.humpLine(field);
        if (StringUtils.endsWithIgnoreCase(this.where, "or)")) {
            this.where.insert(this.where.length() - 1, " " + field + " < ?");
            int last = this.where.lastIndexOf("(");
            conditons.add(this.where.substring(last));
        } else {
            this.where.append(field).append(" < ?");
            conditons.add(field + " < ?");
        }
        paramList.add(value);
        return null;
    };

    public Wrapper gt(String field, Object value) {
        setAnd();
        field = simpleName + Constant.DOT + BaseEntity.humpLine(field);
        if (StringUtils.endsWithIgnoreCase(this.where, "or)")) {
            this.where.insert(this.where.length() - 1, " " + field + " > ?");
            int last = this.where.lastIndexOf("(");
            conditons.add(this.where.substring(last));
        } else {
            this.where.append(field).append(" > ?");
            conditons.add(field + " > ?");
        }
        paramList.add(value);
        return null;
    };

    public Wrapper ge(String field, Object value) {
        setAnd();
        field = simpleName + Constant.DOT + BaseEntity.humpLine(field);
        if (StringUtils.endsWithIgnoreCase(this.where, "or)")) {
            this.where.insert(this.where.length() - 1, " " + field + " >= ?");
            int last = this.where.lastIndexOf("(");
            conditons.add(this.where.substring(last));
        } else {
            this.where.append(field).append(" >= ?");
            conditons.add(field + " >= ?");
        }
        paramList.add(value);
        return null;
    };

    public Wrapper le(String field, Object value) {
        setAnd();
        field = simpleName + Constant.DOT + BaseEntity.humpLine(field);
        if (StringUtils.endsWithIgnoreCase(this.where, "or)")) {
            this.where.insert(this.where.length() - 1, " " + field + " <= ?");
            int last = this.where.lastIndexOf("(");
            conditons.add(this.where.substring(last));
        } else {
            this.where.append(field).append(" <= ?");
            conditons.add(field + " <= ?");
        }
        paramList.add(value);
        return null;
    };

    public Wrapper isNull(String field) {
        field = simpleName + Constant.DOT + BaseEntity.humpLine(field);
        if (StringUtils.endsWithIgnoreCase(this.where, "or)")) {
            this.where.insert(this.where.length() - 1, " " + field + " is null");
            int last = this.where.lastIndexOf("(");
            conditons.add(this.where.substring(last));
        } else {
            this.where.append(field).append(" is null ");
        }
        return null;
    };

    public Wrapper isNotNull(String field) {
        field = simpleName + Constant.DOT + BaseEntity.humpLine(field);
        if (StringUtils.endsWithIgnoreCase(this.where, "or)")) {
            this.where.insert(this.where.length() - 1, " " + field + " is not null");
            int last = this.where.lastIndexOf("(");
            conditons.add(this.where.substring(last));
        } else {
            this.where.append(field).append(" is not null");
        }
        return null;
    };

    public List<Object> getParamList() {
        return paramList;
    }

    public void setCls(Class<?> cls) {
        this.cls = cls;
        this.simpleName = cls.getSimpleName();
    }

    public Class<?> getCls() {
        return cls;
    }

    public int[] getLimit() {
        return limit;
    }

    public List<String> getColumns() {
        return columns;
    }

    public List<String> getOutColumns() {
        return outColumns;
    }

    public void setOutColumns(List<String> outColumns) {
        this.outColumns = outColumns;
    }

    public String getSimpleName() {
        return simpleName;
    }

    public void setSimpleName(String simpleName) {
        this.simpleName = simpleName;
    }

    public StringBuffer getOrderBy() {
        return orderBy;
    }
    /**
     * 默认以and连接查询条件
     */
    public void setAnd() {
        if(StringUtils.endsWithIgnoreCase(this.where, "or)")) {
            return;
        }
        if(!StringUtils.endsWithIgnoreCase(this.where, "and ") &&
                !StringUtils.endsWithIgnoreCase(this.where, "or ") &&
                this.where.length() > 0) {
            this.where.append(Constant.BLANK_AND);
        }
    }

    public StringBuffer getWhere() {
        return this.where;
    }

    /**
     * 包装类之间的复制方法
     *
     * @param wrapper
     */
    public void copy(Wrapper wrapper) {
        this.where = BeanUtils.deepClone(wrapper.getWhere());
        this.paramList = BeanUtils.deepClone(wrapper.getParamList());
        this.oneFieldInFields = wrapper.getOneFieldInFields();
        this.outFields = wrapper.getOutFields();
        this.limit = BeanUtils.deepClone(wrapper.getLimit());
    };

    public void setColumns(List<String> columns) {
        this.columns = columns;
    }

    /**
     * 参数转化成wrapper的条件
     *
     * @param params
     * @return
     */
    public Wrapper mapToWrapper(Map<String, Object> params) {
        // 添加创建时间的时间段删选查询
        String[] overcomes = new String[]{"_t", "limit", "page", "pageSize", "order", "orderField", "limit"};
        this.ge("createDate", params.get("startTime"));
        this.le("createDate", params.get("endTime"));
        this.orderBy("id", "desc");
        a:
        for (Map.Entry<String, Object> entry : params.entrySet()) {
            String[] keys = entry.getKey().split("_");
            String value = String.valueOf(entry.getValue()).trim();
            switch (keys[0]) {
                case Constant.PAGE:
                    this.limit[0] = (int) ConvertUtils.convertType(value, Integer.class);
                    break;
                case Constant.PAGESIZE:
                    this.limit[1] = (int) ConvertUtils.convertType(value, Integer.class);
                    break;
                case Constant.LEFT_LIKE:
                    this.likeL(keys[1], value);
                    break;
                case Constant.RIGHT_LIKE:
                    this.likeR(keys[1], value);
                    break;
                case Constant.ALL_LIKE:
                    this.like(keys[1], value);
                    break;

                case Constant.NOT_EQU:
                    this.uEq(keys[1], value);
                    break;
                case Constant.LE:
                    this.le(keys[1], value);
                    break;
                case Constant.GE:
                    this.ge(keys[1], value);
                    break;
                case Constant.ORDER_BY:
                    if ("ESC".equals(value)) {
                        this.orderBy(keys[1], " ");
                    } else {
                        this.orderBy(keys[1], entry.getValue().toString());
                    }
                    break;
                default:
                    b:for (String over : overcomes) {
                        if (over.equals(value)) {
                            continue a;
                        }
                    }
                    if (this.fields.indexOf(entry.getKey()) != -1) {
                        this.eq(entry.getKey(), value);
                    }
            }
        }
        return null;
    };

    public List<String> getFields() {
        return fields;
    }

    public void setFields(List<String> fields) {
        this.fields = fields;
    }

    public List<Field> getFieldList() {
        return fieldList;
    }

    public void setFieldList(List<Field> fieldList) {
        this.fieldList = fieldList;
    }

    public Field[] getOneToOneFields() {
        return oneToOneFields;
    }

    public void setOneToOneFields(Field[] oneToOneFields) {
        this.oneToOneFields = oneToOneFields;
    }

    public Field[] getOneToMulFields() {
        return oneToMulFields;
    }

    public void setOneToMulFields(Field[] oneToMulFields) {
        this.oneToMulFields = oneToMulFields;
    }

    public Field[] getOneFieldInFields() {
        return oneFieldInFields;
    }

    public void setOneFieldInFields(Field[] oneFieldInFields) {
        this.oneFieldInFields = oneFieldInFields;
    }

    public Field[] getOutFields() {
        return outFields;
    }

    public void setOutFields(Field[] outFields) {
        this.outFields = outFields;
    }

    /**
     * 清空wrapper的使用痕迹
     */
    public void clear() {
        this.sql.setLength(0);
        this.cls = null;
        this.columns.clear();
        this.fields.clear();
        this.outColumns.clear();
        this.where.setLength(0);
        this.orderBy.setLength(0);
        this.limit[0] = -1;
        this.limit[1] = -1;
        this.paramList.clear();
        this.conditons.clear();
        this.simpleName = null;
    };

}
